Quick Intro to R Notebooks and R Markdown
This is an R Markdown Notebook. It combines markdown, a plain text formatting syntax, and R code. When you execute R code within the notebook, the output appears beneath the code.
This file was created in RStudio by going to File…New File…R Notebook.
R code needs to be in “chunks” for it to work. Below is an example of an R code chunk. It makes a parabola using base R graphics (not ggplot2).
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter (Win/Linux) or Cmd+Shift+Return (Mac).
x <- seq(-1, 1, by = 0.01)
y <- x^2
plot(x, yy, type = "l")
To hide the output, click the Expand/Collapse output button. To clear results (or an error), click the “x”.
You can also press Ctrl+Enter (Win/Linux) or Cmd+Return (Mac) to run one line of code at a time (instead of the entire chunk).
Add a new R code chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I (Win/Linux) or Cmd+Option+I (Mac).
Exercise 0 (code along)
Insert a new R code chunk below and type/run the code: 22/7
Import data
Let’s import some data which we’ll visualize today using ggplot2. The data we’ll work with is Albemarle County real estate data which was downloaded from the Office of Geographic Data Services. We’ll use a random sample of the data.
homes <- readRDS("data/homes.Rds")
cannot open compressed file 'data/homes.Rds', probable reason 'No such file or directory'Error in gzfile(file, "rb") : cannot open the connection
Let’s look at the first few rows:
head(homes)
Variable name definitions:
- yearbuilt: year house was built
- finsqft: size of house in number square feet
- cooling: ‘Central Air’ versus ‘No Central Air’
- bedroom: number of bedrooms
- fullbath: number of full bathrooms (toilet, sink and bath)
- halfbath: number of half bathrooms (toilet and sink only)
- lotsize: size of land on which home is located, in acres
- totalvalue: total assessed value of home and property
- esdistrict: the elementary school the home feeds into
- msdistrict: the middle school the home feeds into
- hsdistrict: the high school the home feeds into
- censustract: the census tract the home is located in
- age: of the house in years as of 2018
- condition: assessed condition of home (Substandard, Poor, Fair, Average, Good, Excellent)
- remodel: indicator if house has been remodeled (0=no, 1=yes)
- fp: indicator if house has fireplace (0=no, 1=yes)
Intro to ggplot2
The ggplot2 package implements The Grammar of Graphics as defined in the book of the same name by Leland Wilkinson.
It requires your data be in a data frame or tibble.
Let’s do a quick example. How does the total value of a home relate to its size in finished square feet? Plot totalvalue versus finsqft to create a scatter plot.
First we need to load the ggplot2 package, which we can do with library(tidyverse). Only do this once per R session.

What just happened?
- The
ggplot function creates a plotting area for a data frame
- The
aes function maps variables in the data frame to aesthetic properties of geometric shapes
- The
geom_point function specifies we want to use points
- combine these functions with
+, which adds components to a plot
Don’t forget the parentheses on geom_point()!
Most of what we do with ggplot2 takes this general form
ggplot(data) +
aes() +
geom_something()
It can get much more complicated, but for exploratory visualization this often sufficient.
Exercise 1 (code along)
Do younger (more recently built) houses have a tendency to be bigger than older houses? Create a scatterplot of finsqft (y-axis) versus age (x-axis). Reminder: Insert a new code chunk by pressing Ctrl+Alt+I (Win/Linux) or Cmd+Option+I (Mac). Try entering alpha = 1/5 in geom_point(). What does it do?

More aesthetics and facets
Points not only have positions in a plane, but can be different sizes and have different shapes and colors. We can map variables in our data frame to these aesthetics.
How does the relationship between totalvalue and finsqft differ between homes with and without central air?
Plot totalvalue versus finsqft to create a scatter plot, but also color the points according to whether or not the homes have central air (cooling).

We can also map the size of the points to a variable, such as lotsize. Notice I have mapped the shape of the point to cooling.

Notice the overplotting. Perhaps we prefer to see separate scatterplots for each cooling category. We can achieve that with facets. The function to use is facet_wrap() with the variable that we want to facet on. In this case it’s cooling. Notice we precede the variable with a tilde ~. We can read that as “facet by cooling”

We can also have multiple geoms in a plot. Let’s say we want to add a smooth trend line that summarizes the relationship between finsqft and totalvalue:

Exercise 2
How is totalvalue related to lotsize within each high school district?
Create a scatterplot of totalvalue (y-axis) vs lotsize (x-axis), faceted by hsdistrict. Add smooth trend lines to summarize the relationship.

Zooming in on data
Albemarle county has homes with very large totalvalue and lotsize values. Including them in our plots means we “zoom out” to accommodate them which means we’re “far away” from the majority of homes.
To zoom in on data with ggplot2, we use the coord_cartesion() function and define the limits to view with the xlim and/or ylim arguments. Below we zoom in on the y-axis only.

Probably a good idea to tell your audience that your plot is zoomed in and looking at a subset of your data.
The numbers are a little hard to read on the y-axis. We can format them as dollar amounts using the labels argument in the scale_y_continuous() function. To do this we need to load the scales package, which is installed when you install the ggplot2 package.

Exercise 3
Modify your code from Exercise 2 to zoom in on the y-axis to see home prices ranging from 0 to $1,000,000, and zoom in on the x-axis to view homes on lots ranging in size from 0 to 5 acres. Also use the dollar function to format the home prices on the y-axis.

Visualizing counts
Recall the condition variable. We can quickly get counts of each condition using the table function:
One way to visualize counts is with a bar plot. We can create a bar plot in ggplot2 with geom_bar(). Notice it automatically generates the counts for us.

One of the aesthetics of bars is their “fill” color. We can map a variable from our data frame to the fill aesthetic. For example, let’s see counts of conditions for homes with and without central air (cooling).

The default result is to stack the bars. We can set them side-by-side setting the position argument to “dodge”.

The large number of Average homes makes it difficult to see the counts for the other categories. We could use coord_cartesian() to zoom in on the y-axis. An alternative approach is to view the proportion of homes with and without central air within each condition. We can do this by setting the position argument to “fill”.

The y-axis indicates proportions instead of counts. Only about 10% of homes in the substandard condition have central air.
Exercise 4
Given a home’s condition, is it more likely to be in a particular high school district? For example, given all the homes in Excellent condition, is there a higher proportion of them in one of the high school districts?
Create a bar plot of condition (condition on x-axis), filled by hsdistrict. Set position = ‘fill’ so we visualize within each condition the proportion belonging to a high school district.

Numeric values vs groups
How does the totalvalue of homes differ between elementary school districts? One way to visualize this is with a boxplot. We can do this with geom_boxplot.

The x-axis is hard to read. This happens sometimes. One solution is to simply rotate the plot. We can do that with the coord_flip() function. The coord_flip() function also allows us to zoom in on a plot with the xlim and ylim arguments. Let’s zoom in on the y-axis and look at homes less than 2 million in totalvalue.

- The width of the box (along the y axis) represents the middle 50% of the data. It is called the Interquartile Range (IQR).
- The line in the box is the median.
- The top of the box is the 75th percentile.
- The bottom of the box is the 25th percentile.
- The “whiskers” extending out of the box are to the highest and lowest values not to exceed 1.5 * IQR
- The dots are considered “outliers” relative to the rest of the data.
A similar plot is the violin plot, which allows us to compare the distributions between categories using smooth density plots. Use geom_violin()

Boxplots and violin plots obscure the total number within in each group. That’s not necessarily a bad thing. Too much information in a plot can be overwhelming. However we could create a 1-dimensional scatterplot, sometimes called a “stripchart”. The position_jitter functions adds a little random noise to data to help spread them out.

Exercise 5
How are the ages of homes distributed between the elementary school districts? Are there some elementary schools that serve mostly newer homes? Create a boxplot of age of home by esdistrict.

Line plots over time
How many homes have been built each year in Albemarle county? What year saw the most homes built? We can use the yearbuilt variable in our data to help answer this. We just need to count up the number of homes per yearbuilt and save the result as a data frame. We’ll use what we learned in the last session to do this.
Let’s plot n vs yearbuilt using a line.

There was a boom sometime after 1750. Which year was it? Around 1950 it seems the number of homes built per year really started to take off. When was the peak? It also looks like we saw a dip sometime after 2000. What year was that?
The above plot shows some interesting trends and events, but it’s hard to get precise years and numbers. Fortunately there are ways to make ggplot graphs interactive. Here’s one way using the plotly package. Simply call ggplotly() immediately after your ggplot code:
Now we can interact with the plot to see information of interest. Hovering over points reveals precise information. We can also zoom in on portions of the plot. (Double-click in the plotting region to return to the full plot.)
plotly is package that allows you to make interactive web graphics.
How has lotsize changed over time? Let’s plot median lotsize by yearbuilt versus yearbuilt. Again we need to make a new data frame that contains yearbuilt and median lotsize per year.

After around 1900 it looks most new homes are built on small lots.
Let’s make the plot interactive. This time we save the plot and then call ggplotly on the saved object
Exercise 6
How has the mean number of fullbaths in a home changed over time (yearbuilt), since 1950? (Of course a home built with 1 bathroom in 1950 may not still have just 1 bathroom…)
The following code creates a data frame of mean fullbath by yearbuilt for 1950 to present.
Create a line plot showing how the mean number of fullbaths has changed over time (yearbuilt). Try using ggplotly().
ggplotly()
`geom_smooth()` using method = 'loess' and formula 'y ~ x'
A package that extends ggplot2: ggridges
There are many packages that extend ggplot, mainly by adding additional geoms or themes. One that is useful for our data is the ggridges package.
First notice the challenge of making comparisons of density histograms between 20 census tracts. (Census tracts are small subdivisions of a county that are used by the USA Census Bureau for statistical reporting.)

The ggridges package allows us to create ridgeline plots, which are density plots arranged in a staggered fashion. Notice the new geom geom_density_ridges().

The ggridges package provides a custom theme specifically for use with ridgeline plots. Just tack on the function theme_ridges() to use it.

We can reorder the levels of censustract by a summary statistic such as the median of totalvalue. Notice we use the base R function reorder to accomplish this. That will produce a ridgeline plot where the distributions are ordered by median totalvalue of each census tract.

NOTE: ggplotly doesn’t work with plots made by ggridges.
Creating maps in ggplot2
When we talk about real estate and census tracts, it’s natural to want to see visualizations that incorporate maps. Where is census tract 110 in Albemarle county? How big is it geographically?
To create maps we usually need latitude and longitude values so we an draw shapes, like the borders of counties, states and census tracts. Our data doesn’t have that information so we need to get it. Fortunately there is an R package that helps us quickly get this data. The tigris package allows us to download TIGER/Line shapefiles from the United States Census Bureau and load into R as “sf” objects. SF stands for “simple features” which is standardized way of encoding spatial vector data in computers.
The ggplot2 package has geoms for “sf” objects to help us easily render a map. Let’s draw a map of Albemarle county with census tract borders. First we use the tigris package to download the shapefile data using the tracts function. We save the data as an object called “alb” and draw a map using geom_sf. The theme_void function removes the grid points and background.

That’s nice but it would help if the census tracts were labeled. We can do that with geom_sf_text. We need to map the NAME column of the alb data frame to the label aesthetic.

Now we can see most of the labels, but there’s still some overplotting. It would be nice if we could zoom in on the map. Let’s make the map interactive using the ggplotly function. Click and drag within the map to zoom in on a region.
ggplotly()
st_point_on_surface may not give correct results for longitude/latitude data
We can shade the census tract regions according to a summary statistic such as median total value of a home. Let’s do that! First we calculate median totalvalue by census tract, and then merge into the alb data frame.
To shade the census tract regions we set the fill aesthetic to totalvalue. Hover over the census tract label to see the exact median home value.
ggplotly()
st_point_on_surface may not give correct results for longitude/latitude data
Wrap up
We just covered the basics of ggplot2 today. The way to get better at using ggplot2 is to keep using it and Googling for help when you get stuck.
Two more topics worth mentioning:
- Changing the appearance of plots
- Saving plots as jpg, png, tif, etc.
The appearance of plots are dictated by themes. Some different themes are included with ggplot2. For example, theme_minimal(). Simply tack it on to the end of a plot.

There are whole R packages that provided nothing but more themes for ggplot2. For example, see the ggthemes package.
Saving plots can be done with the ggsave() function. By default, it will save the last plot you created as a 7 in. x 7 in. image.
ggsave("es_lotsize.png")
ggsave() can also be tacked on to the end of ggplot code.
ggplot(homes) +
aes(x = esdistrict, y = lotsize) +
geom_point(position = position_jitter(width = 0.1, height = 0)) +
coord_flip(ylim = c(0,50)) +
theme_minimal() +
ggsave("es_lotsize.jpeg", width = 20, height = 20, units = "cm")
When working in an R script (as opposed to an Rmd file), plots appear in the Plot frame within RStudio which provides a convenient Export button to interactively save plots.
We’re done!
If you would like to talk more about ggplot2 or anything else statistics related, I would love to hear from you: jcf2d@virginia.edu or statlab@virginia.edu
Want more ggplot2? Google getting started with ggplot2 or ggplot2 tutorial. Also see the numerous examples provided with the ggplot2 documentation.
LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbiBpbiBSIHdpdGggZ2dwbG90MiINCmF1dGhvcjogIkNsYXkgRm9yZCwgU3RhdGlzdGljYWwgUmVzZWFyY2ggQ29uc3VsdGFudCwgVVZBIExpYnJhcnkiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCiMjIFF1aWNrIEludHJvIHRvIFIgTm90ZWJvb2tzIGFuZCBSIE1hcmtkb3duDQoNClRoaXMgaXMgYW4gUiBNYXJrZG93biBOb3RlYm9vay4gSXQgY29tYmluZXMgbWFya2Rvd24sIGEgcGxhaW4gdGV4dCBmb3JtYXR0aW5nIHN5bnRheCwgYW5kIFIgY29kZS4gV2hlbiB5b3UgZXhlY3V0ZSBSIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIG91dHB1dCBhcHBlYXJzIGJlbmVhdGggdGhlIGNvZGUuDQoNClRoaXMgZmlsZSB3YXMgY3JlYXRlZCBpbiBSU3R1ZGlvIGJ5IGdvaW5nIHRvIEZpbGUuLi5OZXcgRmlsZS4uLlIgTm90ZWJvb2suDQoNClIgY29kZSBuZWVkcyB0byBiZSBpbiAiY2h1bmtzIiBmb3IgaXQgdG8gd29yay4gQmVsb3cgaXMgYW4gZXhhbXBsZSBvZiBhbiBSIGNvZGUgY2h1bmsuIEl0IG1ha2VzIGEgcGFyYWJvbGEgdXNpbmcgYmFzZSBSIGdyYXBoaWNzIChub3QgZ2dwbG90MikuDQoNClRyeSBleGVjdXRpbmcgdGhpcyBjaHVuayBieSBjbGlja2luZyB0aGUgKlJ1biogYnV0dG9uIHdpdGhpbiB0aGUgY2h1bmsgb3IgYnkgcGxhY2luZyB5b3VyIGN1cnNvciBpbnNpZGUgaXQgYW5kIHByZXNzaW5nICpDdHJsK1NoaWZ0K0VudGVyKiAoV2luL0xpbnV4KSBvciAqQ21kK1NoaWZ0K1JldHVybiogKE1hYykuDQoNCmBgYHtyfQ0KeCA8LSBzZXEoLTEsIDEsIGJ5ID0gMC4wMSkNCnkgPC0geF4yDQpwbG90KHgsIHl5LCB0eXBlID0gImwiKQ0KYGBgDQoNClRvIGhpZGUgdGhlIG91dHB1dCwgY2xpY2sgdGhlIEV4cGFuZC9Db2xsYXBzZSBvdXRwdXQgYnV0dG9uLiBUbyBjbGVhciByZXN1bHRzIChvciBhbiBlcnJvciksIGNsaWNrIHRoZSAieCIuDQoNCllvdSBjYW4gYWxzbyBwcmVzcyAqQ3RybCtFbnRlciogKFdpbi9MaW51eCkgb3IgKkNtZCtSZXR1cm4qIChNYWMpIHRvIHJ1biBvbmUgbGluZSBvZiBjb2RlIGF0IGEgdGltZSAoaW5zdGVhZCBvZiB0aGUgZW50aXJlIGNodW5rKS4NCg0KQWRkIGEgbmV3IFIgY29kZSBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKiAoV2luL0xpbnV4KSBvciAqQ21kK09wdGlvbitJKiAoTWFjKS4NCg0KIyMgRXhlcmNpc2UgMCAoY29kZSBhbG9uZykNCg0KSW5zZXJ0IGEgbmV3IFIgY29kZSBjaHVuayBiZWxvdyBhbmQgdHlwZS9ydW4gdGhlIGNvZGU6IDIyLzcNCg0KDQojIyBJbXBvcnQgZGF0YQ0KDQpMZXQncyBpbXBvcnQgc29tZSBkYXRhIHdoaWNoIHdlJ2xsIHZpc3VhbGl6ZSB0b2RheSB1c2luZyBnZ3Bsb3QyLiBUaGUgZGF0YSB3ZSdsbCB3b3JrIHdpdGggaXMgQWxiZW1hcmxlIENvdW50eSByZWFsIGVzdGF0ZSBkYXRhIHdoaWNoIHdhcyBkb3dubG9hZGVkIGZyb20gdGhlIE9mZmljZSBvZiBHZW9ncmFwaGljIERhdGEgU2VydmljZXMuIFdlJ2xsIHVzZSBhIHJhbmRvbSBzYW1wbGUgb2YgdGhlIGRhdGEuDQoNCmBgYHtyfQ0KVVJMIDwtICJodHRwczovL2dpdGh1Yi5jb20vdXZhc3RhdGxhYi9waGRwbHVzMjAyMC9yYXcvbWFzdGVyL2RhdGEvaG9tZXMucmRzIg0KaG9tZXMgPC0gcmVhZFJEUygiLi4vZGF0YS9ob21lcy5yZHMiKQ0KDQpgYGANCg0KTGV0J3MgbG9vayBhdCB0aGUgZmlyc3QgZmV3IHJvd3M6DQoNCmBgYHtyfQ0KaGVhZChob21lcykNCmBgYA0KDQpWYXJpYWJsZSBuYW1lIGRlZmluaXRpb25zOg0KDQotICAgKnllYXJidWlsdCo6IHllYXIgaG91c2Ugd2FzIGJ1aWx0DQotICAgKmZpbnNxZnQqOiBzaXplIG9mIGhvdXNlIGluIG51bWJlciBzcXVhcmUgZmVldA0KLSAgICpjb29saW5nKjogJ0NlbnRyYWwgQWlyJyB2ZXJzdXMgJ05vIENlbnRyYWwgQWlyJw0KLSAgICpiZWRyb29tKjogbnVtYmVyIG9mIGJlZHJvb21zDQotICAgKmZ1bGxiYXRoKjogbnVtYmVyIG9mIGZ1bGwgYmF0aHJvb21zICh0b2lsZXQsIHNpbmsgYW5kIGJhdGgpDQotICAgKmhhbGZiYXRoKjogbnVtYmVyIG9mIGhhbGYgYmF0aHJvb21zICh0b2lsZXQgYW5kIHNpbmsgb25seSkNCi0gICAqbG90c2l6ZSo6IHNpemUgb2YgbGFuZCBvbiB3aGljaCBob21lIGlzIGxvY2F0ZWQsIGluIGFjcmVzDQotICAgKnRvdGFsdmFsdWUqOiB0b3RhbCBhc3Nlc3NlZCB2YWx1ZSBvZiBob21lIGFuZCBwcm9wZXJ0eQ0KLSAgICplc2Rpc3RyaWN0KjogdGhlIGVsZW1lbnRhcnkgc2Nob29sIHRoZSBob21lIGZlZWRzIGludG8NCi0gICAqbXNkaXN0cmljdCo6IHRoZSBtaWRkbGUgc2Nob29sIHRoZSBob21lIGZlZWRzIGludG8NCi0gICAqaHNkaXN0cmljdCo6IHRoZSBoaWdoIHNjaG9vbCB0aGUgaG9tZSBmZWVkcyBpbnRvDQotICAgKmNlbnN1c3RyYWN0KjogdGhlIGNlbnN1cyB0cmFjdCB0aGUgaG9tZSBpcyBsb2NhdGVkIGluDQotICAgKmFnZSo6IG9mIHRoZSBob3VzZSBpbiB5ZWFycyBhcyBvZiAyMDE4DQotICAgKmNvbmRpdGlvbio6IGFzc2Vzc2VkIGNvbmRpdGlvbiBvZiBob21lIChTdWJzdGFuZGFyZCwgUG9vciwgRmFpciwgQXZlcmFnZSwgR29vZCwgRXhjZWxsZW50KQ0KLSAgICpyZW1vZGVsKjogaW5kaWNhdG9yIGlmIGhvdXNlIGhhcyBiZWVuIHJlbW9kZWxlZCAoMD1ubywgMT15ZXMpDQotICAgKmZwKjogaW5kaWNhdG9yIGlmIGhvdXNlIGhhcyBmaXJlcGxhY2UgKDA9bm8sIDE9eWVzKQ0KDQojIyBJbnRybyB0byBnZ3Bsb3QyDQoNClRoZSBnZ3Bsb3QyIHBhY2thZ2UgaW1wbGVtZW50cyAqVGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MqIGFzIGRlZmluZWQgaW4gdGhlIGJvb2sgb2YgdGhlIHNhbWUgbmFtZSBieSBMZWxhbmQgV2lsa2luc29uLg0KDQpJdCByZXF1aXJlcyB5b3VyIGRhdGEgYmUgaW4gYSBkYXRhIGZyYW1lIG9yIHRpYmJsZS4NCg0KTGV0J3MgZG8gYSBxdWljayBleGFtcGxlLiBIb3cgZG9lcyB0aGUgdG90YWwgdmFsdWUgb2YgYSBob21lIHJlbGF0ZSB0byBpdHMgc2l6ZSBpbiBmaW5pc2hlZCBzcXVhcmUgZmVldD8gUGxvdCB0b3RhbHZhbHVlIHZlcnN1cyBmaW5zcWZ0IHRvIGNyZWF0ZSBhIHNjYXR0ZXIgcGxvdC4NCg0KRmlyc3Qgd2UgbmVlZCB0byBsb2FkIHRoZSBnZ3Bsb3QyIHBhY2thZ2UsIHdoaWNoIHdlIGNhbiBkbyB3aXRoIGBsaWJyYXJ5KHRpZHl2ZXJzZSlgLiBPbmx5IGRvIHRoaXMgb25jZSBwZXIgUiBzZXNzaW9uLg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKSAjIGxvYWRzIGdncGxvdDIgYW5kIDcgb3RoZXIgcGFja2FnZXMNCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IGZpbnNxZnQsIHkgPSB0b3RhbHZhbHVlKSArDQogIGdlb21fcG9pbnQoKSANCmBgYA0KDQpXaGF0IGp1c3QgaGFwcGVuZWQ/DQoNCi0gICBUaGUgYGdncGxvdGAgZnVuY3Rpb24gY3JlYXRlcyBhIHBsb3R0aW5nIGFyZWEgZm9yIGEgZGF0YSBmcmFtZQ0KLSAgIFRoZSBgYWVzYCBmdW5jdGlvbiBtYXBzIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBmcmFtZSB0byBhZXN0aGV0aWMgcHJvcGVydGllcyBvZiBnZW9tZXRyaWMgc2hhcGVzDQotICAgVGhlIGBnZW9tX3BvaW50YCBmdW5jdGlvbiBzcGVjaWZpZXMgd2Ugd2FudCB0byB1c2UgcG9pbnRzDQotICAgY29tYmluZSB0aGVzZSBmdW5jdGlvbnMgd2l0aCBgK2AsIHdoaWNoIGFkZHMgY29tcG9uZW50cyB0byBhIHBsb3QNCg0KRG9uJ3QgZm9yZ2V0IHRoZSBwYXJlbnRoZXNlcyBvbiBgZ2VvbV9wb2ludCgpYCENCg0KKk1vc3Qgb2Ygd2hhdCB3ZSBkbyB3aXRoIGdncGxvdDIgdGFrZXMgdGhpcyBnZW5lcmFsIGZvcm0qDQoNCiAgICBnZ3Bsb3QoZGF0YSkgKw0KICAgICAgYWVzKCkgKw0KICAgICAgZ2VvbV9zb21ldGhpbmcoKQ0KDQpJdCBjYW4gZ2V0IG11Y2ggbW9yZSBjb21wbGljYXRlZCwgYnV0IGZvciBleHBsb3JhdG9yeSB2aXN1YWxpemF0aW9uIHRoaXMgb2Z0ZW4gc3VmZmljaWVudC4NCg0KIyMgRXhlcmNpc2UgMSAoY29kZSBhbG9uZykNCg0KRG8geW91bmdlciAobW9yZSByZWNlbnRseSBidWlsdCkgaG91c2VzIGhhdmUgYSB0ZW5kZW5jeSB0byBiZSBiaWdnZXIgdGhhbiBvbGRlciBob3VzZXM/IENyZWF0ZSBhIHNjYXR0ZXJwbG90IG9mIGZpbnNxZnQgKHktYXhpcykgdmVyc3VzIGFnZSAoeC1heGlzKS4gUmVtaW5kZXI6IEluc2VydCBhIG5ldyBjb2RlIGNodW5rIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKiAoV2luL0xpbnV4KSBvciAqQ21kK09wdGlvbitJKiAoTWFjKS4gVHJ5IGVudGVyaW5nIGBhbHBoYSA9IDEvNWAgaW4gYGdlb21fcG9pbnQoKWAuIFdoYXQgZG9lcyBpdCBkbz8NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBhZ2UsIHkgPSBmaW5zcWZ0KSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAxLzUpDQpgYGANCg0KDQojIyBNb3JlIGFlc3RoZXRpY3MgYW5kIGZhY2V0cw0KDQpQb2ludHMgbm90IG9ubHkgaGF2ZSBwb3NpdGlvbnMgaW4gYSBwbGFuZSwgYnV0IGNhbiBiZSBkaWZmZXJlbnQgc2l6ZXMgYW5kIGhhdmUgZGlmZmVyZW50IHNoYXBlcyBhbmQgY29sb3JzLiBXZSBjYW4gbWFwIHZhcmlhYmxlcyBpbiBvdXIgZGF0YSBmcmFtZSB0byB0aGVzZSBhZXN0aGV0aWNzLg0KDQpIb3cgZG9lcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdG90YWx2YWx1ZSBhbmQgZmluc3FmdCBkaWZmZXIgYmV0d2VlbiBob21lcyB3aXRoIGFuZCB3aXRob3V0IGNlbnRyYWwgYWlyPw0KDQpQbG90IHRvdGFsdmFsdWUgdmVyc3VzIGZpbnNxZnQgdG8gY3JlYXRlIGEgc2NhdHRlciBwbG90LCBidXQgYWxzbyBjb2xvciB0aGUgcG9pbnRzIGFjY29yZGluZyB0byB3aGV0aGVyIG9yIG5vdCB0aGUgaG9tZXMgaGF2ZSBjZW50cmFsIGFpciAoY29vbGluZykuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gZmluc3FmdCwgeSA9IHRvdGFsdmFsdWUsIGNvbG9yID0gY29vbGluZykgKw0KICBnZW9tX3BvaW50KCkgDQpgYGANCg0KV2UgY2FuIGFsc28gbWFwIHRoZSBzaXplIG9mIHRoZSBwb2ludHMgdG8gYSB2YXJpYWJsZSwgc3VjaCBhcyBsb3RzaXplLiBOb3RpY2UgSSBoYXZlIG1hcHBlZCB0aGUgc2hhcGUgb2YgdGhlIHBvaW50IHRvIGNvb2xpbmcuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gZmluc3FmdCwgeSA9IHRvdGFsdmFsdWUsIHNoYXBlID0gY29vbGluZywgc2l6ZSA9IGxvdHNpemUpICsNCiAgZ2VvbV9wb2ludCgpIA0KDQpgYGANCg0KTm90aWNlIHRoZSBvdmVycGxvdHRpbmcuIFBlcmhhcHMgd2UgcHJlZmVyIHRvIHNlZSBzZXBhcmF0ZSBzY2F0dGVycGxvdHMgZm9yIGVhY2ggY29vbGluZyBjYXRlZ29yeS4gV2UgY2FuIGFjaGlldmUgdGhhdCB3aXRoICpmYWNldHMqLiBUaGUgZnVuY3Rpb24gdG8gdXNlIGlzIGBmYWNldF93cmFwKClgIHdpdGggdGhlIHZhcmlhYmxlIHRoYXQgd2Ugd2FudCB0byBmYWNldCBvbi4gSW4gdGhpcyBjYXNlIGl0J3MgY29vbGluZy4gTm90aWNlIHdlIHByZWNlZGUgdGhlIHZhcmlhYmxlIHdpdGggYSB0aWxkZSBgfmAuIFdlIGNhbiByZWFkIHRoYXQgYXMgImZhY2V0IGJ5IGNvb2xpbmciDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gZmluc3FmdCwgeSA9IHRvdGFsdmFsdWUsIHNpemUgPSBsb3RzaXplKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAxLzIpICsNCiAgZmFjZXRfd3JhcCh+IGNvb2xpbmcpDQoNCmBgYA0KDQpXZSBjYW4gYWxzbyBoYXZlIG11bHRpcGxlIGdlb21zIGluIGEgcGxvdC4gTGV0J3Mgc2F5IHdlIHdhbnQgdG8gYWRkIGEgc21vb3RoIHRyZW5kIGxpbmUgdGhhdCBzdW1tYXJpemVzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBmaW5zcWZ0IGFuZCB0b3RhbHZhbHVlOg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IGZpbnNxZnQsIHkgPSB0b3RhbHZhbHVlKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAxLzIpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGZhY2V0X3dyYXAofiBjb29saW5nKSANCg0KYGBgDQoNCiMjIEV4ZXJjaXNlIDINCg0KSG93IGlzIHRvdGFsdmFsdWUgcmVsYXRlZCB0byBsb3RzaXplIHdpdGhpbiBlYWNoIGhpZ2ggc2Nob29sIGRpc3RyaWN0Pw0KDQpDcmVhdGUgYSBzY2F0dGVycGxvdCBvZiB0b3RhbHZhbHVlICh5LWF4aXMpIHZzIGxvdHNpemUgKHgtYXhpcyksIGZhY2V0ZWQgYnkgaHNkaXN0cmljdC4gQWRkIHNtb290aCB0cmVuZCBsaW5lcyB0byBzdW1tYXJpemUgdGhlIHJlbGF0aW9uc2hpcC4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBsb3RzaXplLCB5ID0gdG90YWx2YWx1ZSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh+aHNkaXN0cmljdCkNCmBgYA0KDQoNCiMjIyBab29taW5nIGluIG9uIGRhdGENCg0KQWxiZW1hcmxlIGNvdW50eSBoYXMgaG9tZXMgd2l0aCB2ZXJ5IGxhcmdlIHRvdGFsdmFsdWUgYW5kIGxvdHNpemUgdmFsdWVzLiBJbmNsdWRpbmcgdGhlbSBpbiBvdXIgcGxvdHMgbWVhbnMgd2UgInpvb20gb3V0IiB0byBhY2NvbW1vZGF0ZSB0aGVtIHdoaWNoIG1lYW5zIHdlJ3JlICJmYXIgYXdheSIgZnJvbSB0aGUgbWFqb3JpdHkgb2YgaG9tZXMuDQoNClRvIHpvb20gaW4gb24gZGF0YSB3aXRoIGdncGxvdDIsIHdlIHVzZSB0aGUgYGNvb3JkX2NhcnRlc2lvbigpYCBmdW5jdGlvbiBhbmQgZGVmaW5lIHRoZSBsaW1pdHMgdG8gdmlldyB3aXRoIHRoZSBgeGxpbWAgYW5kL29yIGB5bGltYCBhcmd1bWVudHMuIEJlbG93IHdlIHpvb20gaW4gb24gdGhlIHktYXhpcyBvbmx5Lg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IGZpbnNxZnQsIHkgPSB0b3RhbHZhbHVlKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKCkgKyANCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsNTAwMCksIA0KICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMmU2KSkgIyBmcm9tIDAgdG8gJDIsMDAwLDAwMA0KDQpgYGANClByb2JhYmx5IGEgZ29vZCBpZGVhIHRvIHRlbGwgeW91ciBhdWRpZW5jZSB0aGF0IHlvdXIgcGxvdCBpcyB6b29tZWQgaW4gYW5kIGxvb2tpbmcgYXQgYSBzdWJzZXQgb2YgeW91ciBkYXRhLg0KDQpUaGUgbnVtYmVycyBhcmUgYSBsaXR0bGUgaGFyZCB0byByZWFkIG9uIHRoZSB5LWF4aXMuIFdlIGNhbiBmb3JtYXQgdGhlbSBhcyBkb2xsYXIgYW1vdW50cyB1c2luZyB0aGUgbGFiZWxzIGFyZ3VtZW50IGluIHRoZSBgc2NhbGVfeV9jb250aW51b3VzKClgIGZ1bmN0aW9uLiBUbyBkbyB0aGlzIHdlIG5lZWQgdG8gbG9hZCB0aGUgc2NhbGVzIHBhY2thZ2UsIHdoaWNoIGlzIGluc3RhbGxlZCB3aGVuIHlvdSBpbnN0YWxsIHRoZSBnZ3Bsb3QyIHBhY2thZ2UuDQoNCmBgYHtyfQ0KbGlicmFyeShzY2FsZXMpICMgZm9yIHRoZSBkb2xsYXIgZnVuY3Rpb24NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IGZpbnNxZnQsIHkgPSB0b3RhbHZhbHVlKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgMmU2KSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1kb2xsYXIpIA0KDQpgYGANCg0KIyMgRXhlcmNpc2UgMw0KDQpNb2RpZnkgeW91ciBjb2RlIGZyb20gRXhlcmNpc2UgMiB0byB6b29tIGluIG9uIHRoZSB5LWF4aXMgdG8gc2VlIGhvbWUgcHJpY2VzIHJhbmdpbmcgZnJvbSAwIHRvIGAkMSwwMDAsMDAwYCwgYW5kIHpvb20gaW4gb24gdGhlIHgtYXhpcyB0byB2aWV3IGhvbWVzIG9uIGxvdHMgcmFuZ2luZyBpbiBzaXplIGZyb20gMCB0byA1IGFjcmVzLiBBbHNvIHVzZSB0aGUgZG9sbGFyIGZ1bmN0aW9uIHRvIGZvcm1hdCB0aGUgaG9tZSBwcmljZXMgb24gdGhlIHktYXhpcy4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBsb3RzaXplLCB5ID0gdG90YWx2YWx1ZSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh+IGhzZGlzdHJpY3QpICArDQogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLDUpLCB5bGltID0gYygwLDFlNikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcikNCg0KYGBgDQoNCiMjIFZpc3VhbGl6aW5nIGNvdW50cw0KDQpSZWNhbGwgdGhlIGNvbmRpdGlvbiB2YXJpYWJsZS4gV2UgY2FuIHF1aWNrbHkgZ2V0IGNvdW50cyBvZiBlYWNoIGNvbmRpdGlvbiB1c2luZyB0aGUgdGFibGUgZnVuY3Rpb246DQoNCmBgYHtyfQ0KaG9tZXMgJT4lIGNvdW50KGNvbmRpdGlvbikNCmBgYA0KDQpPbmUgd2F5IHRvIHZpc3VhbGl6ZSBjb3VudHMgaXMgd2l0aCBhIGJhciBwbG90LiBXZSBjYW4gY3JlYXRlIGEgYmFyIHBsb3QgaW4gZ2dwbG90MiB3aXRoIGBnZW9tX2JhcigpYC4gTm90aWNlIGl0IGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGVzIHRoZSBjb3VudHMgZm9yIHVzLg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IGNvbmRpdGlvbikgKw0KICBnZW9tX2JhcigpDQpgYGANCg0KT25lIG9mIHRoZSBhZXN0aGV0aWNzIG9mIGJhcnMgaXMgdGhlaXIgImZpbGwiIGNvbG9yLiBXZSBjYW4gbWFwIGEgdmFyaWFibGUgZnJvbSBvdXIgZGF0YSBmcmFtZSB0byB0aGUgZmlsbCBhZXN0aGV0aWMuIEZvciBleGFtcGxlLCBsZXQncyBzZWUgY291bnRzIG9mIGNvbmRpdGlvbnMgZm9yIGhvbWVzIHdpdGggYW5kIHdpdGhvdXQgY2VudHJhbCBhaXIgKGNvb2xpbmcpLg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IGNvbmRpdGlvbiwgZmlsbCA9IGNvb2xpbmcpICsNCiAgZ2VvbV9iYXIoKQ0KDQpgYGANCg0KVGhlIGRlZmF1bHQgcmVzdWx0IGlzIHRvIHN0YWNrIHRoZSBiYXJzLiBXZSBjYW4gc2V0IHRoZW0gc2lkZS1ieS1zaWRlIHNldHRpbmcgdGhlIHBvc2l0aW9uIGFyZ3VtZW50IHRvICJkb2RnZSIuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gY29uZGl0aW9uLCBmaWxsID0gY29vbGluZykgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpIA0KDQpgYGANCg0KVGhlIGxhcmdlIG51bWJlciBvZiBBdmVyYWdlIGhvbWVzIG1ha2VzIGl0IGRpZmZpY3VsdCB0byBzZWUgdGhlIGNvdW50cyBmb3IgdGhlIG90aGVyIGNhdGVnb3JpZXMuIFdlIGNvdWxkIHVzZSBgY29vcmRfY2FydGVzaWFuKClgIHRvIHpvb20gaW4gb24gdGhlIHktYXhpcy4gQW4gYWx0ZXJuYXRpdmUgYXBwcm9hY2ggaXMgdG8gdmlldyB0aGUgcHJvcG9ydGlvbiBvZiBob21lcyB3aXRoIGFuZCB3aXRob3V0IGNlbnRyYWwgYWlyIHdpdGhpbiBlYWNoIGNvbmRpdGlvbi4gV2UgY2FuIGRvIHRoaXMgYnkgc2V0dGluZyB0aGUgcG9zaXRpb24gYXJndW1lbnQgdG8gImZpbGwiLg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IGNvbmRpdGlvbiwgZmlsbCA9IGNvb2xpbmcpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpDQoNCmBgYA0KDQpUaGUgeS1heGlzIGluZGljYXRlcyBwcm9wb3J0aW9ucyBpbnN0ZWFkIG9mIGNvdW50cy4gT25seSBhYm91dCAxMCUgb2YgaG9tZXMgaW4gdGhlIHN1YnN0YW5kYXJkIGNvbmRpdGlvbiBoYXZlIGNlbnRyYWwgYWlyLg0KDQojIyBFeGVyY2lzZSA0DQoNCkdpdmVuIGEgaG9tZSdzIGNvbmRpdGlvbiwgaXMgaXQgbW9yZSBsaWtlbHkgdG8gYmUgaW4gYSBwYXJ0aWN1bGFyIGhpZ2ggc2Nob29sIGRpc3RyaWN0PyBGb3IgZXhhbXBsZSwgZ2l2ZW4gYWxsIHRoZSBob21lcyBpbiBFeGNlbGxlbnQgY29uZGl0aW9uLCBpcyB0aGVyZSBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIHRoZW0gaW4gb25lIG9mIHRoZSBoaWdoIHNjaG9vbCBkaXN0cmljdHM/DQoNCkNyZWF0ZSBhIGJhciBwbG90IG9mIGNvbmRpdGlvbiAoY29uZGl0aW9uIG9uIHgtYXhpcyksIGZpbGxlZCBieSBoc2Rpc3RyaWN0LiBTZXQgcG9zaXRpb24gPSAnZmlsbCcgc28gd2UgdmlzdWFsaXplIHdpdGhpbiBlYWNoIGNvbmRpdGlvbiB0aGUgcHJvcG9ydGlvbiBiZWxvbmdpbmcgdG8gYSBoaWdoIHNjaG9vbCBkaXN0cmljdC4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBjb25kaXRpb24sIGZpbGwgPSBoc2Rpc3RyaWN0KSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIHlsYWIoIlByb3BvcnRpb24iKQ0KYGBgDQoNCg0KIyMgTnVtZXJpYyB2YWx1ZXMgdnMgZ3JvdXBzDQoNCkhvdyBkb2VzIHRoZSB0b3RhbHZhbHVlIG9mIGhvbWVzIGRpZmZlciBiZXR3ZWVuIGVsZW1lbnRhcnkgc2Nob29sIGRpc3RyaWN0cz8gT25lIHdheSB0byB2aXN1YWxpemUgdGhpcyBpcyB3aXRoIGEgYm94cGxvdC4gV2UgY2FuIGRvIHRoaXMgd2l0aCBgZ2VvbV9ib3hwbG90YC4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBlc2Rpc3RyaWN0LCB5ID0gdG90YWx2YWx1ZSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQoNClRoZSB4LWF4aXMgaXMgaGFyZCB0byByZWFkLiBUaGlzIGhhcHBlbnMgc29tZXRpbWVzLiBPbmUgc29sdXRpb24gaXMgdG8gc2ltcGx5IHJvdGF0ZSB0aGUgcGxvdC4gV2UgY2FuIGRvIHRoYXQgd2l0aCB0aGUgYGNvb3JkX2ZsaXAoKWAgZnVuY3Rpb24uIFRoZSBgY29vcmRfZmxpcCgpYCBmdW5jdGlvbiBhbHNvIGFsbG93cyB1cyB0byB6b29tIGluIG9uIGEgcGxvdCB3aXRoIHRoZSB4bGltIGFuZCB5bGltIGFyZ3VtZW50cy4gTGV0J3Mgem9vbSBpbiBvbiB0aGUgeS1heGlzIGFuZCBsb29rIGF0IGhvbWVzIGxlc3MgdGhhbiAyIG1pbGxpb24gaW4gdG90YWx2YWx1ZS4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBlc2Rpc3RyaWN0LCB5ID0gdG90YWx2YWx1ZSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGNvb3JkX2ZsaXAoeWxpbSA9IGMoMCwyZTYpKQ0KDQpgYGANCg0KLSAgIFRoZSB3aWR0aCBvZiB0aGUgYm94IChhbG9uZyB0aGUgeSBheGlzKSByZXByZXNlbnRzIHRoZSBtaWRkbGUgNTAlIG9mIHRoZSBkYXRhLiBJdCBpcyBjYWxsZWQgdGhlIEludGVycXVhcnRpbGUgUmFuZ2UgKElRUikuDQotICAgVGhlIGxpbmUgaW4gdGhlIGJveCBpcyB0aGUgbWVkaWFuLg0KLSAgIFRoZSB0b3Agb2YgdGhlIGJveCBpcyB0aGUgNzV0aCBwZXJjZW50aWxlLg0KLSAgIFRoZSBib3R0b20gb2YgdGhlIGJveCBpcyB0aGUgMjV0aCBwZXJjZW50aWxlLg0KLSAgIFRoZSAid2hpc2tlcnMiIGV4dGVuZGluZyBvdXQgb2YgdGhlIGJveCBhcmUgdG8gdGhlIGhpZ2hlc3QgYW5kIGxvd2VzdCB2YWx1ZXMgbm90IHRvIGV4Y2VlZCAxLjUgXCogSVFSDQotICAgVGhlIGRvdHMgYXJlIGNvbnNpZGVyZWQgIm91dGxpZXJzIiByZWxhdGl2ZSB0byB0aGUgcmVzdCBvZiB0aGUgZGF0YS4NCg0KQSBzaW1pbGFyIHBsb3QgaXMgdGhlIHZpb2xpbiBwbG90LCB3aGljaCBhbGxvd3MgdXMgdG8gY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9ucyBiZXR3ZWVuIGNhdGVnb3JpZXMgdXNpbmcgc21vb3RoIGRlbnNpdHkgcGxvdHMuIFVzZSBgZ2VvbV92aW9saW4oKWANCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBlc2Rpc3RyaWN0LCB5ID0gdG90YWx2YWx1ZSkgKw0KICBnZW9tX3Zpb2xpbigpICsNCiAgY29vcmRfZmxpcCh5bGltID0gYygwLDFlNikpDQpgYGANCg0KQm94cGxvdHMgYW5kIHZpb2xpbiBwbG90cyBvYnNjdXJlIHRoZSB0b3RhbCBudW1iZXIgd2l0aGluIGluIGVhY2ggZ3JvdXAuIFRoYXQncyBub3QgbmVjZXNzYXJpbHkgYSBiYWQgdGhpbmcuIFRvbyBtdWNoIGluZm9ybWF0aW9uIGluIGEgcGxvdCBjYW4gYmUgb3ZlcndoZWxtaW5nLiBIb3dldmVyIHdlIGNvdWxkIGNyZWF0ZSBhIDEtZGltZW5zaW9uYWwgc2NhdHRlcnBsb3QsIHNvbWV0aW1lcyBjYWxsZWQgYSAic3RyaXBjaGFydCIuIFRoZSBgcG9zaXRpb25faml0dGVyYCBmdW5jdGlvbnMgYWRkcyBhIGxpdHRsZSByYW5kb20gbm9pc2UgdG8gZGF0YSB0byBoZWxwIHNwcmVhZCB0aGVtIG91dC4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBlc2Rpc3RyaWN0LCB5ID0gdG90YWx2YWx1ZSkgKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4yLCBoZWlnaHQgPSAwKSwNCiAgICAgICAgICAgICBhbHBoYSA9IDEvNikgKw0KICBjb29yZF9mbGlwKHlsaW0gPSBjKDAsMWU2KSkNCg0KYGBgDQoNCiMjIEV4ZXJjaXNlIDUNCg0KSG93IGFyZSB0aGUgYWdlcyBvZiBob21lcyBkaXN0cmlidXRlZCBiZXR3ZWVuIHRoZSBlbGVtZW50YXJ5IHNjaG9vbCBkaXN0cmljdHM/IEFyZSB0aGVyZSBzb21lIGVsZW1lbnRhcnkgc2Nob29scyB0aGF0IHNlcnZlIG1vc3RseSBuZXdlciBob21lcz8gQ3JlYXRlIGEgYm94cGxvdCBvZiBhZ2Ugb2YgaG9tZSBieSBlc2Rpc3RyaWN0Lg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IGVzZGlzdHJpY3QsIHkgPSBhZ2UpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQoNCiMjIExpbmUgcGxvdHMgb3ZlciB0aW1lDQoNCkhvdyBtYW55IGhvbWVzIGhhdmUgYmVlbiBidWlsdCBlYWNoIHllYXIgaW4gQWxiZW1hcmxlIGNvdW50eT8gV2hhdCB5ZWFyIHNhdyB0aGUgbW9zdCBob21lcyBidWlsdD8gV2UgY2FuIHVzZSB0aGUgeWVhcmJ1aWx0IHZhcmlhYmxlIGluIG91ciBkYXRhIHRvIGhlbHAgYW5zd2VyIHRoaXMuIFdlIGp1c3QgbmVlZCB0byBjb3VudCB1cCB0aGUgbnVtYmVyIG9mIGhvbWVzIHBlciB5ZWFyYnVpbHQgYW5kIHNhdmUgdGhlIHJlc3VsdCBhcyBhIGRhdGEgZnJhbWUuIFdlJ2xsIHVzZSB3aGF0IHdlIGxlYXJuZWQgaW4gdGhlIGxhc3Qgc2Vzc2lvbiB0byBkbyB0aGlzLiANCg0KYGBge3J9DQpob21lc195ZWFyIDwtIGhvbWVzICU+JSAgDQogIGNvdW50KHllYXJidWlsdCkgDQp0YWlsKGhvbWVzX3llYXIsIG4gPSAxMCkNCmBgYA0KDQpMZXQncyBwbG90IG4gdnMgeWVhcmJ1aWx0IHVzaW5nIGEgbGluZS4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXNfeWVhcikgKw0KICBhZXMoeCA9IHllYXJidWlsdCwgeSA9IG4pICsNCiAgZ2VvbV9saW5lKCkNCmBgYA0KDQpUaGVyZSB3YXMgYSBib29tIHNvbWV0aW1lIGFmdGVyIDE3NTAuIFdoaWNoIHllYXIgd2FzIGl0PyBBcm91bmQgMTk1MCBpdCBzZWVtcyB0aGUgbnVtYmVyIG9mIGhvbWVzIGJ1aWx0IHBlciB5ZWFyIHJlYWxseSBzdGFydGVkIHRvIHRha2Ugb2ZmLiBXaGVuIHdhcyB0aGUgcGVhaz8gSXQgYWxzbyBsb29rcyBsaWtlIHdlIHNhdyBhIGRpcCBzb21ldGltZSBhZnRlciAyMDAwLiBXaGF0IHllYXIgd2FzIHRoYXQ/DQoNClRoZSBhYm92ZSBwbG90IHNob3dzIHNvbWUgaW50ZXJlc3RpbmcgdHJlbmRzIGFuZCBldmVudHMsIGJ1dCBpdCdzIGhhcmQgdG8gZ2V0IHByZWNpc2UgeWVhcnMgYW5kIG51bWJlcnMuIEZvcnR1bmF0ZWx5IHRoZXJlIGFyZSB3YXlzIHRvIG1ha2UgZ2dwbG90IGdyYXBocyBpbnRlcmFjdGl2ZS4gSGVyZSdzIG9uZSB3YXkgdXNpbmcgdGhlIHBsb3RseSBwYWNrYWdlLiBTaW1wbHkgY2FsbCBgZ2dwbG90bHkoKWAgaW1tZWRpYXRlbHkgYWZ0ZXIgeW91ciBnZ3Bsb3QgY29kZToNCg0KYGBge3J9DQpsaWJyYXJ5KHBsb3RseSkNCmdncGxvdChob21lc195ZWFyKSArDQogIGFlcyh4ID0geWVhcmJ1aWx0LCB5ID0gbikgKw0KICBnZW9tX2xpbmUoKQ0KZ2dwbG90bHkoKQ0KYGBgDQoNCk5vdyB3ZSBjYW4gaW50ZXJhY3Qgd2l0aCB0aGUgcGxvdCB0byBzZWUgaW5mb3JtYXRpb24gb2YgaW50ZXJlc3QuIEhvdmVyaW5nIG92ZXIgcG9pbnRzIHJldmVhbHMgcHJlY2lzZSBpbmZvcm1hdGlvbi4gV2UgY2FuIGFsc28gem9vbSBpbiBvbiBwb3J0aW9ucyBvZiB0aGUgcGxvdC4gKERvdWJsZS1jbGljayBpbiB0aGUgcGxvdHRpbmcgcmVnaW9uIHRvIHJldHVybiB0byB0aGUgZnVsbCBwbG90LikNCg0KcGxvdGx5IGlzIHBhY2thZ2UgdGhhdCBhbGxvd3MgeW91IHRvIG1ha2UgaW50ZXJhY3RpdmUgd2ViIGdyYXBoaWNzLiAgDQoNCkhvdyBoYXMgbG90c2l6ZSBjaGFuZ2VkIG92ZXIgdGltZT8gTGV0J3MgcGxvdCBtZWRpYW4gbG90c2l6ZSBieSB5ZWFyYnVpbHQgdmVyc3VzIHllYXJidWlsdC4gQWdhaW4gd2UgbmVlZCB0byBtYWtlIGEgbmV3IGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyB5ZWFyYnVpbHQgYW5kIG1lZGlhbiBsb3RzaXplIHBlciB5ZWFyLg0KDQpgYGB7cn0NCmhvbWVzX21lZGlhbl9sb3RzaXplIDwtIGhvbWVzICU+JSANCiAgZ3JvdXBfYnkoeWVhcmJ1aWx0KSAlPiUgDQogIHN1bW1hcml6ZShsb3RzaXplID0gbWVkaWFuKGxvdHNpemUpKQ0KDQpnZ3Bsb3QoaG9tZXNfbWVkaWFuX2xvdHNpemUpICsNCiAgYWVzKHggPSB5ZWFyYnVpbHQsIHkgPSBsb3RzaXplKSArDQogIGdlb21fbGluZSgpIA0KYGBgDQoNCkFmdGVyIGFyb3VuZCAxOTAwIGl0IGxvb2tzIG1vc3QgbmV3IGhvbWVzIGFyZSBidWlsdCBvbiBzbWFsbCBsb3RzLg0KDQpMZXQncyBtYWtlIHRoZSBwbG90IGludGVyYWN0aXZlLiBUaGlzIHRpbWUgd2Ugc2F2ZSB0aGUgcGxvdCBhbmQgdGhlbiBjYWxsIGBnZ3Bsb3RseWAgb24gdGhlIHNhdmVkIG9iamVjdA0KDQpgYGB7cn0NCnAgPC0gZ2dwbG90KGhvbWVzX21lZGlhbl9sb3RzaXplKSArDQogIGFlcyh4ID0geWVhcmJ1aWx0LCB5ID0gbG90c2l6ZSkgKw0KICBnZW9tX2xpbmUoKSANCmdncGxvdGx5KHApDQoNCmBgYA0KDQojIyBFeGVyY2lzZSA2DQoNCkhvdyBoYXMgdGhlIG1lYW4gbnVtYmVyIG9mIGZ1bGxiYXRocyBpbiBhIGhvbWUgY2hhbmdlZCBvdmVyIHRpbWUgKHllYXJidWlsdCksIHNpbmNlIDE5NTA/IChPZiBjb3Vyc2UgYSBob21lIGJ1aWx0IHdpdGggMSBiYXRocm9vbSBpbiAxOTUwIG1heSBub3Qgc3RpbGwgaGF2ZSBqdXN0IDEgYmF0aHJvb20uLi4pDQoNClRoZSBmb2xsb3dpbmcgY29kZSBjcmVhdGVzIGEgZGF0YSBmcmFtZSBvZiBtZWFuIGZ1bGxiYXRoIGJ5IHllYXJidWlsdCBmb3IgMTk1MCB0byBwcmVzZW50Lg0KDQpgYGB7cn0NCmhvbWVzX21lYW5fZmIgPC0gaG9tZXMgJT4lIA0KICBmaWx0ZXIoeWVhcmJ1aWx0ID49IDE5NTApICU+JSANCiAgZ3JvdXBfYnkoeWVhcmJ1aWx0KSAlPiUgDQogIHN1bW1hcml6ZShmdWxsYmF0aCA9IG1lYW4oZnVsbGJhdGgpKQ0KICANCmhlYWQoaG9tZXNfbWVhbl9mYikNCmBgYA0KDQpDcmVhdGUgYSBsaW5lIHBsb3Qgc2hvd2luZyBob3cgdGhlIG1lYW4gbnVtYmVyIG9mIGZ1bGxiYXRocyBoYXMgY2hhbmdlZCBvdmVyIHRpbWUgKHllYXJidWlsdCkuIFRyeSB1c2luZyBgZ2dwbG90bHkoKWAuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzX21lYW5fZmIpICsNCiAgYWVzKHggPSB5ZWFyYnVpbHQsIHkgPSBmdWxsYmF0aCkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fc21vb3RoKCkNCmdncGxvdGx5KCkNCmBgYA0KDQoNCg0KIyMgQSBwYWNrYWdlIHRoYXQgZXh0ZW5kcyBnZ3Bsb3QyOiBnZ3JpZGdlcw0KDQpUaGVyZSBhcmUgbWFueSBwYWNrYWdlcyB0aGF0IGV4dGVuZCBnZ3Bsb3QsIG1haW5seSBieSBhZGRpbmcgYWRkaXRpb25hbCBnZW9tcyBvciB0aGVtZXMuIE9uZSB0aGF0IGlzIHVzZWZ1bCBmb3Igb3VyIGRhdGEgaXMgdGhlIGdncmlkZ2VzIHBhY2thZ2UuDQoNCkZpcnN0IG5vdGljZSB0aGUgY2hhbGxlbmdlIG9mIG1ha2luZyBjb21wYXJpc29ucyBvZiBkZW5zaXR5IGhpc3RvZ3JhbXMgYmV0d2VlbiAyMCBjZW5zdXMgdHJhY3RzLiAoQ2Vuc3VzIHRyYWN0cyBhcmUgc21hbGwgc3ViZGl2aXNpb25zIG9mIGEgY291bnR5IHRoYXQgYXJlIHVzZWQgYnkgdGhlIFVTQSBDZW5zdXMgQnVyZWF1IGZvciBzdGF0aXN0aWNhbCByZXBvcnRpbmcuKQ0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IHRvdGFsdmFsdWUpICsNCiAgZ2VvbV9kZW5zaXR5KCkgKw0KICBmYWNldF93cmFwKH5jZW5zdXN0cmFjdCkgKw0KICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMWU2KSkNCg0KYGBgDQoNClRoZSBnZ3JpZGdlcyBwYWNrYWdlIGFsbG93cyB1cyB0byBjcmVhdGUgcmlkZ2VsaW5lIHBsb3RzLCB3aGljaCBhcmUgZGVuc2l0eSBwbG90cyBhcnJhbmdlZCBpbiBhIHN0YWdnZXJlZCBmYXNoaW9uLiBOb3RpY2UgdGhlIG5ldyBnZW9tIGBnZW9tX2RlbnNpdHlfcmlkZ2VzKClgLg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dyaWRnZXMpDQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSB0b3RhbHZhbHVlLCB5ID0gY2Vuc3VzdHJhY3QpICsgDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArDQogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxZTYpKQ0KDQpgYGANCg0KDQpUaGUgZ2dyaWRnZXMgcGFja2FnZSBwcm92aWRlcyBhIGN1c3RvbSB0aGVtZSBzcGVjaWZpY2FsbHkgZm9yIHVzZSB3aXRoIHJpZGdlbGluZSBwbG90cy4gSnVzdCB0YWNrIG9uIHRoZSBmdW5jdGlvbiBgdGhlbWVfcmlkZ2VzKClgIHRvIHVzZSBpdC4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSB0b3RhbHZhbHVlLCB5ID0gZmFjdG9yKGNlbnN1c3RyYWN0KSkgKyANCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcygpICsNCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDFlNikpICsNCiAgdGhlbWVfcmlkZ2VzKCkNCg0KYGBgDQoNCg0KV2UgY2FuIHJlb3JkZXIgdGhlIGxldmVscyBvZiBjZW5zdXN0cmFjdCBieSBhIHN1bW1hcnkgc3RhdGlzdGljIHN1Y2ggYXMgdGhlIG1lZGlhbiBvZiB0b3RhbHZhbHVlLiBOb3RpY2Ugd2UgdXNlIHRoZSBiYXNlIFIgZnVuY3Rpb24gYHJlb3JkZXJgIHRvIGFjY29tcGxpc2ggdGhpcy4gVGhhdCB3aWxsIHByb2R1Y2UgYSByaWRnZWxpbmUgcGxvdCB3aGVyZSB0aGUgIGRpc3RyaWJ1dGlvbnMgYXJlIG9yZGVyZWQgYnkgbWVkaWFuIHRvdGFsdmFsdWUgb2YgZWFjaCBjZW5zdXMgdHJhY3QuDQoNCmBgYHtyfQ0KZ2dwbG90KGhvbWVzKSArDQogIGFlcyh4ID0gdG90YWx2YWx1ZSwgeSA9IHJlb3JkZXIoY2Vuc3VzdHJhY3QsIHRvdGFsdmFsdWUsIG1lZGlhbikpICsgDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArDQogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAxZTYpKSArDQogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmRvbGxhcikgKw0KICBsYWJzKHkgPSAiQ2Vuc3VzIFRyYWN0IiwgeCA9ICJUb3RhbCBWYWx1ZSIsIA0KICAgICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBUb3RhbCBWYWx1ZSBieSBDZW5zdXMgVHJhY3QiKSArDQogIHRoZW1lX3JpZGdlcygpDQoNCmBgYA0KDQpOT1RFOiBgZ2dwbG90bHlgIGRvZXNuJ3Qgd29yayB3aXRoIHBsb3RzIG1hZGUgYnkgZ2dyaWRnZXMuDQoNCiMjIENyZWF0aW5nIG1hcHMgaW4gZ2dwbG90Mg0KDQpXaGVuIHdlIHRhbGsgYWJvdXQgcmVhbCBlc3RhdGUgYW5kIGNlbnN1cyB0cmFjdHMsIGl0J3MgbmF0dXJhbCB0byB3YW50IHRvIHNlZSB2aXN1YWxpemF0aW9ucyB0aGF0IGluY29ycG9yYXRlIG1hcHMuIFdoZXJlIGlzIGNlbnN1cyB0cmFjdCAxMTAgaW4gQWxiZW1hcmxlIGNvdW50eT8gSG93IGJpZyBpcyBpdCBnZW9ncmFwaGljYWxseT8NCg0KVG8gY3JlYXRlIG1hcHMgd2UgdXN1YWxseSBuZWVkIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgdmFsdWVzIHNvIHdlIGFuIGRyYXcgc2hhcGVzLCBsaWtlIHRoZSBib3JkZXJzIG9mIGNvdW50aWVzLCBzdGF0ZXMgYW5kIGNlbnN1cyB0cmFjdHMuIE91ciBkYXRhIGRvZXNuJ3QgaGF2ZSB0aGF0IGluZm9ybWF0aW9uIHNvIHdlIG5lZWQgdG8gZ2V0IGl0LiBGb3J0dW5hdGVseSB0aGVyZSBpcyBhbiBSIHBhY2thZ2UgdGhhdCBoZWxwcyB1cyBxdWlja2x5IGdldCB0aGlzIGRhdGEuIFRoZSB0aWdyaXMgcGFja2FnZSBhbGxvd3MgdXMgdG8gZG93bmxvYWQgVElHRVIvTGluZSBzaGFwZWZpbGVzIGZyb20gdGhlIFVuaXRlZCBTdGF0ZXMgQ2Vuc3VzIEJ1cmVhdSBhbmQgbG9hZCBpbnRvIFIgYXMgInNmIiBvYmplY3RzLiBTRiBzdGFuZHMgZm9yICJzaW1wbGUgZmVhdHVyZXMiIHdoaWNoIGlzIHN0YW5kYXJkaXplZCB3YXkgb2YgZW5jb2Rpbmcgc3BhdGlhbCB2ZWN0b3IgZGF0YSBpbiBjb21wdXRlcnMuDQoNClRoZSBnZ3Bsb3QyIHBhY2thZ2UgaGFzIGdlb21zIGZvciAic2YiIG9iamVjdHMgdG8gaGVscCB1cyBlYXNpbHkgcmVuZGVyIGEgbWFwLiBMZXQncyBkcmF3IGEgbWFwIG9mIEFsYmVtYXJsZSBjb3VudHkgd2l0aCBjZW5zdXMgdHJhY3QgYm9yZGVycy4gRmlyc3Qgd2UgdXNlIHRoZSB0aWdyaXMgcGFja2FnZSB0byBkb3dubG9hZCB0aGUgc2hhcGVmaWxlIGRhdGEgdXNpbmcgdGhlIGB0cmFjdHNgIGZ1bmN0aW9uLiBXZSBzYXZlIHRoZSBkYXRhIGFzIGFuIG9iamVjdCBjYWxsZWQgImFsYiIgYW5kIGRyYXcgYSBtYXAgdXNpbmcgYGdlb21fc2ZgLiBUaGUgYHRoZW1lX3ZvaWRgIGZ1bmN0aW9uIHJlbW92ZXMgdGhlIGdyaWQgcG9pbnRzIGFuZCBiYWNrZ3JvdW5kLg0KDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWdyaXMpDQoNCiMgdGhpcyBpcyBnb2luZyB0byBkb3dubG9hZCBhYm91dCAxNSBNQiBvZiBkYXRhOyBtYXkgdGFrZSBhIHNlY29uZA0KYWxiIDwtIHRyYWN0cyhzdGF0ZSA9ICJWQSIsIGNvdW50eSA9ICJBbGJlbWFybGUiKQ0KDQpnZ3Bsb3QoYWxiKSArDQogIGdlb21fc2YoY29sb3IgPSAicmVkIikgKw0KICB0aGVtZV92b2lkKCkgDQpgYGANCg0KDQpUaGF0J3MgbmljZSBidXQgaXQgd291bGQgaGVscCBpZiB0aGUgY2Vuc3VzIHRyYWN0cyB3ZXJlIGxhYmVsZWQuIFdlIGNhbiBkbyB0aGF0IHdpdGggYGdlb21fc2ZfdGV4dGAuIFdlIG5lZWQgdG8gbWFwIHRoZSBOQU1FIGNvbHVtbiBvZiB0aGUgYWxiIGRhdGEgZnJhbWUgdG8gdGhlIGxhYmVsIGFlc3RoZXRpYy4NCg0KYGBge3J9DQpnZ3Bsb3QoYWxiKSArDQogIGFlcyhsYWJlbCA9IE5BTUUpICsNCiAgZ2VvbV9zZihjb2xvciA9ICJyZWQiKSArDQogIGdlb21fc2ZfdGV4dChzaXplID0gMykgKyAjIHNpemUgPSAzIG1ha2VzIHRleHQgc21hbGxlcg0KICB0aGVtZV92b2lkKCkgDQpgYGANCg0KTm93IHdlIGNhbiBzZWUgbW9zdCBvZiB0aGUgbGFiZWxzLCBidXQgdGhlcmUncyBzdGlsbCBzb21lIG92ZXJwbG90dGluZy4gSXQgd291bGQgYmUgbmljZSBpZiB3ZSBjb3VsZCB6b29tIGluIG9uIHRoZSBtYXAuIExldCdzIG1ha2UgdGhlIG1hcCBpbnRlcmFjdGl2ZSB1c2luZyB0aGUgYGdncGxvdGx5YCBmdW5jdGlvbi4gQ2xpY2sgYW5kIGRyYWcgd2l0aGluIHRoZSBtYXAgdG8gem9vbSBpbiBvbiBhIHJlZ2lvbi4NCg0KYGBge3J9DQpnZ3Bsb3QoYWxiKSArDQogIGFlcyhsYWJlbCA9IE5BTUUpICsNCiAgZ2VvbV9zZihjb2xvciA9ICJyZWQiKSArDQogIGdlb21fc2ZfdGV4dChzaXplID0gMykgKw0KICB0aGVtZV92b2lkKCkNCmdncGxvdGx5KCkNCmBgYA0KDQoNCldlIGNhbiBzaGFkZSB0aGUgY2Vuc3VzIHRyYWN0IHJlZ2lvbnMgYWNjb3JkaW5nIHRvIGEgc3VtbWFyeSBzdGF0aXN0aWMgc3VjaCBhcyBtZWRpYW4gdG90YWwgdmFsdWUgb2YgYSBob21lLiBMZXQncyBkbyB0aGF0ISBGaXJzdCB3ZSBjYWxjdWxhdGUgbWVkaWFuIHRvdGFsdmFsdWUgYnkgY2Vuc3VzIHRyYWN0LCBhbmQgdGhlbiBtZXJnZSBpbnRvIHRoZSBhbGIgZGF0YSBmcmFtZS4NCg0KDQpgYGB7cn0NCm1lZF90diA8LSBob21lcyAlPiUNCiAgZ3JvdXBfYnkoY2Vuc3VzdHJhY3QpICU+JSANCiAgc3VtbWFyaXplKHRvdGFsdmFsdWUgPSBtZWRpYW4odG90YWx2YWx1ZSkpDQoNCmFsYiA8LSBsZWZ0X2pvaW4oYWxiLCBtZWRfdHYsIGJ5ID0gYygiTkFNRSIgPSAiY2Vuc3VzdHJhY3QiKSkNCg0KYGBgDQoNClRvIHNoYWRlIHRoZSBjZW5zdXMgdHJhY3QgcmVnaW9ucyB3ZSBzZXQgdGhlIGZpbGwgYWVzdGhldGljIHRvIHRvdGFsdmFsdWUuIEhvdmVyIG92ZXIgdGhlIGNlbnN1cyB0cmFjdCBsYWJlbCB0byBzZWUgdGhlIGV4YWN0IG1lZGlhbiBob21lIHZhbHVlLg0KDQpgYGB7cn0NCmdncGxvdChhbGIpICsNCiAgYWVzKGZpbGwgPSB0b3RhbHZhbHVlLCBsYWJlbCA9IE5BTUUpICsNCiAgZ2VvbV9zZihjb2xvciA9ICJyZWQiKSArDQogIGdlb21fc2ZfdGV4dChzaXplID0gMykgKw0KICBzY2FsZV9maWxsX2NvbnRpbnVvdXMoIk1lZGlhbiBob21lIHZhbHVlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBkb2xsYXIpICsNCiAgdGhlbWVfdm9pZCgpDQpnZ3Bsb3RseSgpDQoNCmBgYA0KDQoNCg0KIyMgV3JhcCB1cA0KDQpXZSBqdXN0IGNvdmVyZWQgdGhlIGJhc2ljcyBvZiBnZ3Bsb3QyIHRvZGF5LiBUaGUgd2F5IHRvIGdldCBiZXR0ZXIgYXQgdXNpbmcgZ2dwbG90MiBpcyB0byBrZWVwIHVzaW5nIGl0IGFuZCBHb29nbGluZyBmb3IgaGVscCB3aGVuIHlvdSBnZXQgc3R1Y2suDQoNClR3byBtb3JlIHRvcGljcyB3b3J0aCBtZW50aW9uaW5nOg0KDQoxLiAgQ2hhbmdpbmcgdGhlIGFwcGVhcmFuY2Ugb2YgcGxvdHMNCjIuICBTYXZpbmcgcGxvdHMgYXMganBnLCBwbmcsIHRpZiwgZXRjLg0KDQpUaGUgYXBwZWFyYW5jZSBvZiBwbG90cyBhcmUgZGljdGF0ZWQgYnkgdGhlbWVzLiBTb21lIGRpZmZlcmVudCB0aGVtZXMgYXJlIGluY2x1ZGVkIHdpdGggZ2dwbG90Mi4gRm9yIGV4YW1wbGUsIGB0aGVtZV9taW5pbWFsKClgLiBTaW1wbHkgdGFjayBpdCBvbiB0byB0aGUgZW5kIG9mIGEgcGxvdC4NCg0KYGBge3J9DQpnZ3Bsb3QoaG9tZXMpICsNCiAgYWVzKHggPSBlc2Rpc3RyaWN0LCB5ID0gbG90c2l6ZSkgKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xLCBoZWlnaHQgPSAwKSkgKw0KICBjb29yZF9mbGlwKHlsaW0gPSBjKDAsNTApKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNClRoZXJlIGFyZSB3aG9sZSBSIHBhY2thZ2VzIHRoYXQgcHJvdmlkZWQgbm90aGluZyBidXQgbW9yZSB0aGVtZXMgZm9yIGdncGxvdDIuIEZvciBleGFtcGxlLCBzZWUgdGhlIGBnZ3RoZW1lc2AgcGFja2FnZS4NCg0KU2F2aW5nIHBsb3RzIGNhbiBiZSBkb25lIHdpdGggdGhlIGBnZ3NhdmUoKWAgZnVuY3Rpb24uIEJ5IGRlZmF1bHQsIGl0IHdpbGwgc2F2ZSB0aGUgbGFzdCBwbG90IHlvdSBjcmVhdGVkIGFzIGEgNyBpbi4geCA3IGluLiBpbWFnZS4NCg0KYGBge3J9DQpnZ3NhdmUoImVzX2xvdHNpemUucG5nIikNCmBgYA0KDQpgZ2dzYXZlKClgIGNhbiBhbHNvIGJlIHRhY2tlZCBvbiB0byB0aGUgZW5kIG9mIGdncGxvdCBjb2RlLg0KDQpgYGB7cn0NCmdncGxvdChob21lcykgKw0KICBhZXMoeCA9IGVzZGlzdHJpY3QsIHkgPSBsb3RzaXplKSArDQogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjEsIGhlaWdodCA9IDApKSArDQogIGNvb3JkX2ZsaXAoeWxpbSA9IGMoMCw1MCkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgZ2dzYXZlKCJlc19sb3RzaXplLmpwZWciLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAyMCwgdW5pdHMgPSAiY20iKQ0KDQpgYGANCg0KV2hlbiB3b3JraW5nIGluIGFuIFIgc2NyaXB0IChhcyBvcHBvc2VkIHRvIGFuIFJtZCBmaWxlKSwgcGxvdHMgYXBwZWFyIGluIHRoZSBQbG90IGZyYW1lIHdpdGhpbiBSU3R1ZGlvIHdoaWNoIHByb3ZpZGVzIGEgY29udmVuaWVudCBFeHBvcnQgYnV0dG9uIHRvIGludGVyYWN0aXZlbHkgc2F2ZSBwbG90cy4NCg0KIyMgV2UncmUgZG9uZSENCg0KSWYgeW91IHdvdWxkIGxpa2UgdG8gdGFsayBtb3JlIGFib3V0IGdncGxvdDIgb3IgYW55dGhpbmcgZWxzZSBzdGF0aXN0aWNzIHJlbGF0ZWQsIEkgd291bGQgbG92ZSB0byBoZWFyIGZyb20geW91OiBgamNmMmRAdmlyZ2luaWEuZWR1YCBvciBgc3RhdGxhYkB2aXJnaW5pYS5lZHVgDQoNCldhbnQgbW9yZSBnZ3Bsb3QyPyBHb29nbGUgYGdldHRpbmcgc3RhcnRlZCB3aXRoIGdncGxvdDJgIG9yIGBnZ3Bsb3QyIHR1dG9yaWFsYC4gQWxzbyBzZWUgdGhlIG51bWVyb3VzIGV4YW1wbGVzIHByb3ZpZGVkIHdpdGggdGhlIGdncGxvdDIgZG9jdW1lbnRhdGlvbi4NCg0KIyMgUmVmZXJlbmNlcw0KDQotICAgV2lja2hhbSwgSC4gKDIwMTYpLCAqZ2dwbG90MjogRWxlZ2FudCBHcmFwaGljcyBmb3IgRGF0YSBBbmFseXNpcyogKDJuZCBlZCksIFNwcmluZ2VyLiA8aHR0cHM6Ly9nZ3Bsb3QyLWJvb2sub3JnLz4NCg0KLSAgIFdpY2toYW0sIEguIGFuZCBHcm9sZW11bmQgRy4gKDIwMTcpLCAqUiBmb3IgRGF0YSBTY2llbmNlKi4gTydSZWlsbHkuIDxodHRwOi8vcjRkcy5oYWQuY28ubnovPg0KDQoqKmdncGxvdDIgY2hlYXQgc2hlZXQqKlwNCjxodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9yYXcvbWFzdGVyL2RhdGEtdmlzdWFsaXphdGlvbi0yLjEucGRmPg0K